package com.gitee.wsl.collections.map

import com.gitee.wsl.collections.delegate.DelegatingMutableMap
import com.gitee.wsl.func.predicate.Predicate


/**
 * Decorates another `Map` to validate that additions
 * match a specified predicate.
 *
 *
 * This map exists to provide validation for the decorated map.
 * It is normally created to decorate an empty map.
 * If an object cannot be added to the map, an IllegalArgumentException is thrown.
 *
 *
 *
 * One usage would be to ensure that no null keys are added to the map.
 *
 * <pre>Map map = PredicatedSet.decorate(new HashMap(), NotNullPredicate.INSTANCE, null);</pre>
 *
 *
 * **Note that PredicatedMap is not synchronized and is not thread-safe.**
 * If you wish to use this map from multiple threads concurrently, you must use
 * appropriate synchronization. The simplest approach is to wrap this map
 * using [java.util.Collections.synchronizedMap]. This class may throw
 * exceptions when accessed by concurrent threads without synchronization.
 *
 *
 *
 * This class is Serializable from Commons Collections 3.1.
 *
 *
 * @param <K> the type of the keys in this map
 * @param <V> the type of the values in this map
 * @since 3.0
</V></K> */
class PredicatedMap<K, V>(
    map: MutableMap<K,V>,
    /** The key predicate to use  */
    protected val keyPredicate: Predicate<in K>?,
    /** The value predicate to use  */
    protected val valuePredicate: Predicate<in V>?,
) : DelegatingMutableMap<K,V>(map){
    /**
     * Constructor that wraps (not copies).
     *
     * @param map  the map to decorate, must not be null
     * @param keyPredicate  the predicate to validate the keys, null means no check
     * @param valuePredicate  the predicate to validate to values, null means no check
     * @throws NullPointerException if the map is null
     */
    init {
        map.forEach{ entry ->
            this.validate(entry)
        }
    }

    /**
     * Override to validate an object set into the map via `setValue`.
     *
     * @param value  the value to validate
     * @return the value itself
     * @throws IllegalArgumentException if invalid
     * @since 3.1
     */
    protected  fun checkSetValue(value: V): V {
        require(valuePredicate?.test(value) == true) { "Cannot set value - Predicate rejected it" }
        return value
    }

    protected val isSetValueChecking: Boolean
        /**
         * Override to only return true when there is a value transformer.
         *
         * @return true if a value predicate is in use
         * @since 3.1
         */
        get() = valuePredicate != null

    public override fun put(key: K, value: V): V? {
        validate(key, value)
        return delegate.put(key, value)
    }

    public override fun putAll(from: Map<out K,  V>) {
        for (entry in from.entries) {
            validate(entry.key, entry.value)
        }
        super.putAll(from)
    }

    /**
     * Deserializes the map in using a custom routine.
     *
     * @param in  the input stream
     * @throws IOException if an error occurs while reading from the stream
     * @throws ClassNotFoundException if an object read from the stream cannot be loaded
     * @since 3.1
     */
//    @Throws(
//        IOException::class,
//        java.lang.ClassNotFoundException::class
//    )  // (1) should only fail if input stream is incorrect
//    private fun readObject(`in`: ObjectInputStream) {
//        `in`.defaultReadObject()
//        map = `in`.readObject() as Map<K,V>? // (1)
//    }

    /**
     * Validates a key value pair.
     *
     * @param key  the key to validate
     * @param value  the value to validate
     * @throws IllegalArgumentException if invalid
     */
    protected fun validate(key: K, value: V) {
        require(!(keyPredicate != null && !keyPredicate.test(key))) { "Cannot add key - Predicate rejected it" }
        require(!(valuePredicate != null && !valuePredicate.test(value))) { "Cannot add value - Predicate rejected it" }
    }

    protected fun validate(entry:Map.Entry<K,V>) = validate(entry.key,entry.value)

    /**
     * Serializes this object to an ObjectOutputStream.
     *
     * @param out the target ObjectOutputStream.
     * @throws IOException thrown when an I/O errors occur writing to the target stream.
     * @since 3.1
     */
//    @Throws(IOException::class)
//    private fun writeObject(out: ObjectOutputStream) {
//        out.defaultWriteObject()
//        out.writeObject(map)
//    }

    companion object {
        /** Serialization version  */
        private const val serialVersionUID = 7412622456128415156L

        /**
         * Factory method to create a predicated (validating) map.
         *
         *
         * If there are any elements already in the list being decorated, they
         * are validated.
         *
         *
         * @param <K>  the key type
         * @param <V>  the value type
         * @param map  the map to decorate, must not be null
         * @param keyPredicate  the predicate to validate the keys, null means no check
         * @param valuePredicate  the predicate to validate to values, null means no check
         * @return a new predicated map
         * @throws NullPointerException if the map is null
         * @since 4.0
        </V></K> */
        fun <K, V> predicatedMap(
            map: MutableMap<K,V>,
            keyPredicate: Predicate<in K>?,
            valuePredicate: Predicate<in V>? = null,
        ): PredicatedMap<K,V> {
            return PredicatedMap(map, keyPredicate, valuePredicate)
        }
    }
}